import querystring from 'querystring'

export default class UploaderUtil {

  static getMSize(pieceSize) {
    return pieceSize * 1024 * 1024
  }

  static getMinPieceSize() {
    return this.getMSize(5)
  }

  /**
   * 上传文件
   */
  static uploadFile({request, uploadUrl, uploadData = {}}, {
    file,
    progress,
    success,
    error
  }) {
    const fetchForm = new FormData()
    fetchForm.append('file', file)
    Object.keys(uploadData).forEach(key => {
      fetchForm.append(key, uploadData[key])
    })
    return request.post(uploadUrl, fetchForm, {}, {
      onUploadProgress: event => {
        let percent = event.loaded / event.total * 100
        percent = percent >= 99 ? 99 : percent
        progress && progress(percent.toFixed(0))
      }
    }).then(data => {
      success && success(data)
    }).catch(e => {
      error && error(e)
    })
  }

  // 分片上传
  /*
  * 分片上传函数 支持多个文件
  * @param options
  * options.file 表示源文件
  * options.pieceSize 表示需要分片的大小 默认是5m
  * options.chunkUrl 分片上传的后端地址
  * options.fileUrl 整个文件的上传地址
  * progress 进度回调
  * success 成功回调
  * error 失败回调
  *
  */
  static uploadByPieces = ({request, shardingUrl = {}, uploadData = {}}, {
    file,
    pieceSize = 5,
    progress,
    success,
    error
  }) => {
    // 上传过程中用到的变量
    let progressNum = 1 // 进度
    let successAllCount = 0 // 上传成功的片数
    let chunkCount = 0
    let uploadId = ''
    let objectName = ''
    const partETags = []
    const uploadParts = []
    const numProcess = 4

    // 获取md5
    const readFileMD5 = (currentFile) => {
      const chunkSize = pieceSize * 1024 * 1024 // 5MB一片
      chunkCount = Math.ceil(currentFile.size / chunkSize) // 总片数
      // 改成同时最多4个上传的

      // 针对单个文件进行chunk上传
      for (let i = 0; i < chunkCount; i++) {
        const {chunk} = getChunkInfo(currentFile, i, chunkSize)
        uploadParts.push({chunk, i})
      }
      startUpload()
    }
    const startUpload = () => {
      // 同时上传2个
      for (let i = 0; i < numProcess; i++) {
        const item = uploadParts.shift()
        if (item) {
          const {chunk, i} = item
          uploadChunk(file, chunk, i)
        } else {
          break
        }
      }
    }
    const getChunkInfo = (file, currentChunk, chunkSize) => {
      const start = currentChunk * chunkSize
      const end = Math.min(file.size, start + chunkSize)
      const chunk = file.slice(start, end)
      return {start, end, chunk}
    }

    // 更新进度
    const progressFun = () => {
      progressNum = Math.ceil(successAllCount / chunkCount * 100)
      progress(progressNum)
    }
    // 对分片已经处理完毕的文件进行上传
    const uploadFile = () => {
      request.post(shardingUrl.uploadTrunkFinish, {
        uploadId,
        partETags,
        objectName
      }).then(data => {
        successAllCount++
        progressFun()
        success && success(data)
      }).catch(e => {
        error && error(e)
      })
    }
    const uploadChunk = (currentFile, chunkData, chunkIndex) => {
      const fetchForm = new FormData()
      fetchForm.append('fileName', currentFile.name)
      fetchForm.append('chunkData', chunkData)
      fetchForm.append('chunkCount', chunkCount)
      fetchForm.append('uploadId', uploadId)
      fetchForm.append('objectName', objectName)
      fetchForm.append('chunkIndex', chunkIndex)

      request.post(shardingUrl.uploadTrunk, fetchForm).then(data => {
        data.partCRC = data.partCRCStr
        partETags.push(data)
        successAllCount++
        progressFun()
        if (successAllCount < chunkCount) {
          if (successAllCount % numProcess === 0) {
            startUpload()
          }
        } else {
          // 当总数大于等于分片个数的时候
          uploadFile()
        }
      }).catch(e => {
        error && error(e)
      })
    }
    request.post(shardingUrl.init, querystring.stringify({name: file.name, ...uploadData})).then(data => {
      uploadId = data.uploadId
      objectName = data.objectName
      readFileMD5(file) // 开始执行代码
    }).catch(e => {
      error && error(e)
    })
  }

  // 初始化分片
  static initTrunk({request, shardingUrl = {}, uploadData = {}, file}) {
    return request.post(shardingUrl.init, querystring.stringify({fileName: file.name, ...uploadData}))
  }
}
